#ifndef LPOOL_H_
#define LPOOL_H_

#include <algorithm>

#include "../ProblemInstance.hpp"

class LPool
{
public:

	int nCli;
	double tolerancia;
	double melhorCusto;

	// pesos para validação
	vector<double> pesos;

	//variaveis da lista
	vector<double> peso;
	vector<double> custo;
	vector<int> periodo;
	vector<vector<int> > rotas;
	vector<vector<int> >* cliInRoute;

	PRVPProblemInstance& pPRVP;

	double calcula_peso(vector<int> r)
	{
		double p = 0;
		for (int i = 0; i < r.size(); i++)
			p += pesos.at(r.at(i));

		return p;
	}

	double calcula_custo(vector<int> r)
	{
		Matrix<double>& dist = (*pPRVP.dist);

		//adicionando o depósito no final para facilitar contas
		r.push_back(0);

		double c = 0;
		//somando a distância do depósito
		c += dist(0, r.at(0));

		//somando demais distâncias
		for (int i = 0; i < r.size() - 1; i++)
		{
			c += dist(r.at(i), r.at(i + 1));
		}

		//removendo depósito
		r.pop_back();

		return c;
	}

public:
	LPool(PRVPProblemInstance& _pPRVP, double _tolerancia) :
		pPRVP(_pPRVP)
	{
		tolerancia = _tolerancia;
		cliInRoute = new vector<vector<int> > (pPRVP.nCli + 1);
		//gerando os pesos
		int peso_max = 100000;
		for (int i = 0; i < pPRVP.nCli + 1; i++)
		{
			pesos.push_back(rand() % peso_max);
		}

		random_shuffle(pesos.begin(), pesos.end());
	}

	virtual ~LPool()
	{
		delete cliInRoute;
	}

	void add(EvaluationPRVP& e, RepPRVP& rep)
	{
		if (rotas.size() <= 0) // se é o primeiro elemento, adiciona
		{
			melhorCusto = e.evaluation();
			for (int p = 0; p < rep.size(); p++)//para todos os periodos
			{
				for (int r = 0; r < rep.at(p).size(); r++)//para todas as rotas
				{
					if (rep.at(p).at(r).size() > 0)//tem pelo menos um cliente
					{
						vector<int>& rota = rep.at(p).at(r);
						rotas.push_back(rota);
						peso.push_back(calcula_peso(rota));
						custo.push_back(calcula_custo(rota));
						periodo.push_back(p);
						//idendificando os clientes dessa rota
						for (int i = 0; i < rota.size(); i++)
						{
							cliInRoute->at(rota.at(i)).push_back(rotas.size() - 1);
						}

					}
				}
			}
		}
		else // a lista já contém itens
		{
			double custoAux = e.evaluation();
			if ( custoAux <= (1.0+tolerancia) * melhorCusto) // só aceitando soluções piores em tolerância% da melhor solução
			{
				if(custoAux < melhorCusto)
					melhorCusto = custoAux;
				for (int p = 0; p < rep.size(); p++)//para todos os periodos
				{
					for (int r = 0; r < rep.at(p).size(); r++)//para todas as rotas
					{
						if (rep.at(p).at(r).size() > 0)//tem pelo menos um cliente
						{
							double carga = 0;
							for (int i = 0; i < rep.at(p).at(r).size(); i++)
								carga += pPRVP.dados->at(rep.at(p).at(r).at(i)).at(DEMANDA);

							if (carga <= pPRVP.capacidade)
							{

								vector<int>& rota = rep.at(p).at(r);

								//variavel de controle
								bool encontrou = false;

								//variaveis temporarias
								double temp_custo = calcula_custo(rota);
								double temp_peso = calcula_peso(rota);

								for (int i = 0; i < rotas.size(); i++)
								{
									if (temp_peso == peso.at(i) && temp_custo == custo.at(i) && rota.size() == rotas.at(i).size())
									{
										encontrou = true;
										break;
									}
								}

								if (!encontrou)
								{
									rotas.push_back(rota);
									peso.push_back(calcula_peso(rota));
									custo.push_back(calcula_custo(rota));
									periodo.push_back(p);
									//idendificando os clientes dessa rota
									for (int i = 0; i < rota.size(); i++)
									{
										cliInRoute->at(rota.at(i)).push_back(rotas.size() - 1);
									}
								}
							}
						}
					}
				}
			}
		}
	}

	void add_withoutTest(SolutionPRVP& s)
	{
		RepPRVP& rep = s.getR();
		for (int p = 0; p < rep.size(); p++)//para todos os periodos
		{
			for (int r = 0; r < rep.at(p).size(); r++)//para todas as rotas
			{
				if (rep.at(p).at(r).size() > 0)//tem pelo menos um cliente
				{
					vector<int>& rota = rep.at(p).at(r);
					rotas.push_back(rota);
					peso.push_back(calcula_peso(rota));
					custo.push_back(calcula_custo(rota));
					periodo.push_back(p);
					//idendificando os clientes dessa rota
					for (int i = 0; i < rota.size(); i++)
					{
						cliInRoute->at(rota.at(i)).push_back(rotas.size() - 1);
					}

				}
			}
		}
	}

	void verifica()
	{
		for (int c = 1; c < pPRVP.nCli + 1; c++)
		{
			cout << "cliente (" << c << ") : ";
			for (int i = 0; i < cliInRoute->at(c).size(); i++)
			{
				int index = cliInRoute->at(c).at(i);
				cout << "\t " << index;
				bool achou = false;
				for (int r = 0; r < rotas.at(index).size(); r++)
				{
					if (rotas.at(index).at(r) == c)
						achou = true;
				}
				if (!achou)
				{
					cout << "Nao achou!!!" << endl;
					cout << "c [" << c << "] : " << rotas.at(index) << endl;
					exit(1);
				}
			}
			cout << endl;
		}
	}

	void print()
	{
		cout << "####### LPool size: " << rotas.size() << endl << endl;
		/*for (int i = 0; i < rotas.size(); i++)
		 {
		 cout << "r[" << i << "] < " << periodo.at(i) << " , " << custo.at(i) << " , " << peso.at(i) << " >" << " " << rotas.at(i) << endl;
		 }*/
	}
};

#endif /* LPOOL_H_ */
